package com.xiaoma.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaoma.config.AlipayTemplate;
import com.xiaoma.dao.OrderDao;
import com.xiaoma.entity.PayVo;
import com.xiaoma.feign.GoodsFeignService;
import com.xiaoma.service.OrderService;
import entity.Payorder;
import exception.NoStockException;
import lombok.SneakyThrows;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import utils.OrderConstant;
import utils.OrderStatusEnum;

import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * @Author: xiaoma
 * @Date: 2021/7/9 10:03
 * @description:
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderDao, Payorder> implements OrderService {
    @Autowired
    private OrderDao orderDao;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private AlipayTemplate alipayTemplate;
    @Autowired
    private GoodsFeignService goodsFeignService;
    @Override
    public void closeOrder(Payorder order) {
        //查询订单最新状态
        Payorder newOrder = baseMapper.selectById(order.getId());
        //如果订单不存在 不执行定时关单 消费完成
        if (newOrder==null) return;
        if (OrderStatusEnum.CREATE_NEW.getCode() == newOrder.getOrderStatus()) {
            //关单
            newOrder.setOrderStatus(OrderStatusEnum.CANCLED.getCode());
            baseMapper.updateById(newOrder);
        }
    }
    /**
     * @Description 创建订单，消息队列实现最终一致性
     * @Author Xiaoma
     * @Date 2021/8/24 16:31
     * @param uid
     * @param gid
     * @param token
     * @return java.lang.String
     **/
    @SneakyThrows
    @Override
    @Transactional
    public String createOrder(String uid,String gid,String token) {
        //验令牌 保证接口幂等性
        //同一页面订单数据只能提交一次
        //防止由于重复点击或者网络重发等情况会导致数据被重复提交
        //新建lua脚本 0 令牌删除失败 1删除成功
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        // 原子验证令牌 删除令牌
        Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
                  //返回值是仍然是一个可变的集合，但是返回值是其内部类，不具有add方法，可以通过set方法进行增加值，默认长度是10
//                Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + uid),
                  //返回的是不可变的集合，但是这个长度的集合只有1，可以减少内存空间。但是返回的值依然是Collections的内部实现类，同样没有add的方法，调用add，set方法会报错
//                Collections.singletonList(OrderConstant.USER_ORDER_TOKEN_PREFIX + uid),
                Stream.of(OrderConstant.USER_ORDER_TOKEN_PREFIX + uid).collect(Collectors.toList()),
                token);
        if (result!=1L) return null;
        //创建订单
        Payorder order = new Payorder();
        order.setUid(Integer.parseInt(uid));
        order.setGid(Integer.parseInt(gid));
        order.setGoodsNum(1);
        order.setOrderStatus(1);
        orderDao.insert(order);
        //锁库存
        Integer num = goodsFeignService.lockStock(gid, 1);
        if (num!=1) {
            //锁库存失败抛异常回滚订单
            throw new NoStockException(gid);
        }
        //锁库存成功发送两个消息
        //给MQ发送消息，加入延时库存解锁队列
        rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",order);
        //给MQ发送消息，加入延时关单队列
        rabbitTemplate.convertAndSend("order-event-exchange", "order.create.order", order);
        return alipayTemplate.pay(new PayVo(order.getId(), "订单1", "12.00", "商品描述"));
    }
}
